package com.example.common.grpc;

import com.example.common.constants.ServiceNames;
import com.example.game.proto.internal.GrpcServiceGrpc;
import io.grpc.ManagedChannel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.context.annotation.DependsOn;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * 服务实例注册器
 * 自动发现和注册服务实例到负载均衡器
 *
 * @author: Allen
 * @create: 2025/7/3 22:50
 * @description: 服务实例注册器
 **/
@Slf4j
@Component
@DependsOn("serviceStartupListener")
public class ServiceInstanceRegistrar {

    @Autowired
    private ServerIdLoadBalancer loadBalancer;

    @Autowired
    private ServerIdGenerator serverIdGenerator;

    @Autowired
    private DiscoveryClient discoveryClient;

    @Autowired
    private DynamicGrpcClientManager dynamicGrpcClientManager;

    // 刷新状态监控
    private final Map<ServiceNames, Long> lastRefreshTime = new ConcurrentHashMap<>();
    private final Map<ServiceNames, Boolean> refreshInProgress = new ConcurrentHashMap<>();
    private final Map<ServiceNames, Integer> refreshFailureCount = new ConcurrentHashMap<>();

    @PostConstruct()
    public void init() {
        log.info("服务实例注册器初始化开始...");

        // 注册当前服务实例
        registerCurrentService();

        // 注册其他服务实例
        registerServiceInstances();

        log.info("服务实例注册器初始化完成");
    }

    /**
     * 注册当前服务实例
     */
    private void registerCurrentService() {
        int currentServerId = serverIdGenerator.getServerId();
        ServiceNames currentServiceType = serverIdGenerator.getServiceType();

        log.info("注册当前服务实例: serverId={}, serviceType={}", currentServerId, currentServiceType);

        // 这里可以根据需要注册当前服务到负载均衡器
        // 通常当前服务不需要注册到自己的负载均衡器中
    }

    /**
     * 注册其他服务实例
     */
    private void registerServiceInstances() {
        // 获取当前服务类型
        ServiceNames currentServiceType = serverIdGenerator.getServiceType();

        // 只注册其他类型的服务实例，不注册当前服务自己的类型
        for (ServiceNames serviceType : ServiceNames.values()) {
            if (serviceType != currentServiceType) {
                log.info("注册服务实例: {}", serviceType);
                registerServiceInstanceByType(serviceType);
            } else {
                log.info("跳过注册当前服务自己的类型: {}", serviceType);
            }
        }
    }

    /**
     * 根据服务类型注册服务实例
     */
    private void registerServiceInstanceByType(ServiceNames serviceType) {
        try {
            // 获取当前服务的服务器ID
            int currentServerId = serverIdGenerator.getServerId();
            ServiceNames currentServiceType = serverIdGenerator.getServiceType();

            // 如果是当前服务自己的同类型实例，则跳过注册
            if (serviceType == currentServiceType) {
                log.info("跳过注册当前服务自己的实例: serviceType={}, currentServerId={}", serviceType, currentServerId);
                return;
            }

            // 从服务发现中获取真实的服务器ID
            List<Integer> serverIds = getServerIdsFromDiscovery(serviceType);

            // 注册所有发现的实例
            for (int serverId : serverIds) {
                registerSingleInstance(serverId, serviceType);
            }

        } catch (Exception e) {
            log.error("注册服务实例失败: serviceType={}", serviceType, e);
        }
    }

    /**
     * 注册单个服务实例
     */
    private void registerSingleInstance(int serverId, ServiceNames serviceType) {
        // 检查是否已经注册过
        if (loadBalancer.getInstanceByServerId(serverId) != null) {
            log.debug("服务器实例已存在，跳过注册: serverId={}, serviceType={}", serverId, serviceType);
            return;
        }

        // 使用DynamicGrpcClientManager获取stub和channel
        GrpcServiceGrpc.GrpcServiceBlockingStub blockingStub =
                dynamicGrpcClientManager.getBlockingStub(serverId, serviceType);
        GrpcServiceGrpc.GrpcServiceStub asyncStub = 
                dynamicGrpcClientManager.getAsyncStub(serverId, serviceType);
        ManagedChannel channel = 
                dynamicGrpcClientManager.getChannel(serverId, serviceType);
        
        if (blockingStub == null || asyncStub == null || channel == null) {
            log.warn("无法为serverId={}的{}服务创建gRPC连接", serverId, serviceType);
            return;
        }
     /*   SocketAddress grpcSocketAddress = dynamicGrpcClientManager.getGrpcSocketAddress(serverId, serviceType);
        if (grpcSocketAddress == null) {
            return;
        }*/
        // 注册到负载均衡器
        //loadBalancer.registerInstance(serverId, serviceType, grpcSocketAddress, 15);
        // 注册到负载均衡器
        loadBalancer.registerInstance(serverId, serviceType, blockingStub, asyncStub, channel);

        log.info("注册服务实例成功: serviceType={}, serverId={}", serviceType, serverId);
    }

    /**
     * 注册特定的服务实例（用于增量更新）
     */
    private void registerSpecificInstances(ServiceNames serviceType, Set<Integer> serverIds) {
        if (serverIds.isEmpty()) {
            return;
        }

        try {
            // 注册指定的实例
            for (Integer serverId : serverIds) {
                registerSingleInstance(serverId, serviceType);
            }

        } catch (Exception e) {
            log.error("注册特定服务实例失败: serviceType={}, serverIds={}", serviceType, serverIds, e);
        }
    }

    /**
     * 从服务发现中获取服务器ID列表
     */
    private List<Integer> getServerIdsFromDiscovery(ServiceNames serviceType) {
        try {
            List<ServiceInstance> instances = discoveryClient.getInstances(serviceType.value());
            log.debug("从服务发现获取到{}个{}服务实例", instances.size(), serviceType);

            // 获取当前服务的服务器ID
            int currentServerId = serverIdGenerator.getServerId();
            ServiceNames currentServiceType = serverIdGenerator.getServiceType();

            return instances.stream()
                    .map(this::extractServerIdFromInstance)
                    .filter(serverId -> serverId > 0)
                    // 排除当前服务自己的同类型实例
                    .filter(serverId -> !isCurrentServiceInstance(serverId, serviceType, currentServerId, currentServiceType))
                    .distinct()
                    .collect(Collectors.toList());

        } catch (Exception e) {
            log.error("从服务发现获取{}服务实例失败", serviceType, e);
            return Collections.emptyList();
        }
    }

    /**
     * 检查是否为当前服务自己的实例
     */
    private boolean isCurrentServiceInstance(int serverId, ServiceNames serviceType,
                                             int currentServerId, ServiceNames currentServiceType) {
        // 如果是同类型服务且服务器ID相同，则认为是当前服务自己
        if (serviceType == currentServiceType && serverId == currentServerId) {
            log.debug("排除当前服务自己的实例: serviceType={}, serverId={}", serviceType, serverId);
            return true;
        }
        return false;
    }

    /**
     * 从服务实例中提取服务器ID
     */
    public int extractServerIdFromInstance(ServiceInstance instance) {
        try {
            // 方法1: 从元数据中获取server.id
            String serverIdStr = instance.getMetadata().get("server.id");
            if (serverIdStr != null && !serverIdStr.isEmpty()) {
                return Integer.parseInt(serverIdStr);
            }

            // 方法2: 从实例ID中解析服务器ID
            String instanceId = instance.getInstanceId();
            if (instanceId != null && instanceId.contains(":")) {
                String[] parts = instanceId.split(":");
                if (parts.length >= 2) {
                    // 假设实例ID格式为: serviceName:serverId:port
                    try {
                        return Integer.parseInt(parts[1]);
                    } catch (NumberFormatException e) {
                        log.debug("无法从实例ID解析服务器ID: {}", instanceId);
                    }
                }
            }

            // 方法3: 从主机名中解析服务器ID
            String host = instance.getHost();
            if (host != null && host.contains("-")) {
                String[] parts = host.split("-");
                if (parts.length > 0) {
                    try {
                        String lastPart = parts[parts.length - 1];
                        if (lastPart.matches("\\d+")) {
                            return Integer.parseInt(lastPart);
                        }
                    } catch (NumberFormatException e) {
                        log.debug("无法从主机名解析服务器ID: {}", host);
                    }
                }
            }

            // 不再通过端口号推断服务器ID
            log.warn("无法从服务实例中提取服务器ID: instanceId={}, host={}, port={}, metadata={}",
                    instance.getInstanceId(), instance.getHost(), instance.getPort(), instance.getMetadata());
            return 0;

        } catch (Exception e) {
            log.error("提取服务器ID失败: instanceId={}", instance.getInstanceId(), e);
            return 0;
        }
    }


    /**
     * 刷新服务实例
     */
    @Scheduled(fixedRate = 30000) // 每分钟刷新一次
    public void refreshServiceInstances() {
        log.debug("开始刷新服务实例...");

        // 获取当前服务类型
        ServiceNames currentServiceType = serverIdGenerator.getServiceType();

        // 只刷新其他类型的服务实例
        for (ServiceNames serviceType : ServiceNames.values()) {
            if (serviceType != currentServiceType) {
                log.debug("刷新服务实例: {}", serviceType);
                refreshServiceInstanceByType(serviceType);
            } else {
                log.debug("跳过刷新当前服务自己的类型: {}", serviceType);
            }
        }

        log.debug("服务实例刷新完成");
    }

    /**
     * 根据服务类型刷新服务实例
     */
    private void refreshServiceInstanceByType(ServiceNames serviceType) {
        // 检查是否正在刷新中
        if (refreshInProgress.getOrDefault(serviceType, false)) {
            log.debug("{}服务正在刷新中，跳过本次刷新", serviceType);
            return;
        }

        try {
            // 标记开始刷新
            refreshInProgress.put(serviceType, true);
            lastRefreshTime.put(serviceType, System.currentTimeMillis());

            log.debug("开始增量刷新服务实例: {}", serviceType);

            // 获取当前已注册的实例
            List<ServerIdLoadBalancer.ServerInstance> existingInstances =
                    loadBalancer.serviceInstances.getOrDefault(serviceType, new ArrayList<>());
            Set<Integer> existingServerIds = existingInstances.stream()
                    .map(ServerIdLoadBalancer.ServerInstance::getServerId)
                    .collect(Collectors.toSet());

            // 从服务发现获取最新实例
            List<Integer> discoveredServerIds = getServerIdsFromDiscovery(serviceType);
            Set<Integer> discoveredServerIdSet = new HashSet<>(discoveredServerIds);

            // 找出需要移除的实例（已不存在于服务发现中）
            Set<Integer> toRemove = new HashSet<>(existingServerIds);
            toRemove.removeAll(discoveredServerIdSet);

            // 找出需要新增的实例（新出现在服务发现中）
            Set<Integer> toAdd = new HashSet<>(discoveredServerIdSet);
            toAdd.removeAll(existingServerIds);

            // 执行增量更新
            if (!toRemove.isEmpty()) {
                log.info("移除{}个过期实例: {}", toRemove.size(), toRemove);
                for (Integer serverId : toRemove) {
                    loadBalancer.unregisterInstance(serverId);
                    // 同时移除gRPC连接
                    dynamicGrpcClientManager.removeConnection(serverId, serviceType);
                }
            }

            if (!toAdd.isEmpty()) {
                log.info("新增{}个实例: {}", toAdd.size(), toAdd);
                // 注册新发现的实例
                registerSpecificInstances(serviceType, toAdd);
            }

            if (toRemove.isEmpty() && toAdd.isEmpty()) {
                log.debug("服务实例无变化: {}", serviceType);
            }

            // 重置失败计数
            refreshFailureCount.put(serviceType, 0);

            log.debug("增量刷新服务实例完成: {} (移除:{}, 新增:{})", serviceType, toRemove.size(), toAdd.size());

        } catch (Exception e) {
            log.error("增量刷新服务实例失败: {}", serviceType, e);

            // 增加失败计数
            int failureCount = refreshFailureCount.getOrDefault(serviceType, 0) + 1;
            refreshFailureCount.put(serviceType, failureCount);

            // 如果连续失败次数过多，回退到全量刷新
            if (failureCount >= 3) {
                log.warn("{}服务连续失败{}次，回退到全量刷新", serviceType, failureCount);
                fallbackToFullRefresh(serviceType);
            }
        } finally {
            // 标记刷新完成
            refreshInProgress.put(serviceType, false);
        }
    }

    /**
     * 回退到全量刷新（当增量更新失败时）
     */
    private void fallbackToFullRefresh(ServiceNames serviceType) {
        try {
            log.warn("增量更新失败，回退到全量刷新: {}", serviceType);

            // 清除旧的实例
            loadBalancer.removeServiceInstances(serviceType);

            // 移除所有gRPC连接
            dynamicGrpcClientManager.removeServiceConnections(serviceType);

            // 重新注册实例
            registerServiceInstanceByType(serviceType);

            log.info("全量刷新服务实例完成: {}", serviceType);
        } catch (Exception e) {
            log.error("全量刷新服务实例也失败: {}", serviceType, e);
        }
    }


    /**
     * 强制刷新指定服务（用于手动触发）
     */
    public void forceRefreshService(ServiceNames serviceType) {
        log.info("强制刷新服务: {}", serviceType);

        // 重置失败计数
        refreshFailureCount.put(serviceType, 0);

        // 执行刷新
        refreshServiceInstanceByType(serviceType);
    }


} 